﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.IO;

namespace Referee
{
    class Program
    {
        static bool firstRun = true;

        static void Main(string[] args)
        {
            var settings = XDocument.Load("Settings.xml").Element("Settings");
            logpath = Path.GetFullPath(settings.Element("LogPath").Value);
            logs("Loading Settings...\r\n");
            var xlangs = settings.Element("Langs").Elements("Lang");
            var langs = new List<JudgeKernel.Lang>();
            foreach (var xlang in xlangs)
            {
                var lang = new JudgeKernel.Lang
                {
                    id = int.Parse(xlang.Element("id").Value),
                    compilerPath = xlang.Element("compilerPath").Value,
                    compilerParam = xlang.Element("compilerParam").Value,
                    codePath = xlang.Element("codePath").Value,
                    exePath = xlang.Element("exePath").Value,
                    executePath = xlang.Element("executePath").Value,
                    executeParam = xlang.Element("executeParam").Value,
                    ceInfoRegExp = xlang.Element("ceInfoRegExp").Value
                };
                langs.Add(lang);
                log("Lang({0}) {1}\r\n", lang.id, lang.compilerPath);
            }
            var heartbeat = int.Parse(settings.Element("Heartbeat").Value);
            var path = Path.GetFullPath(settings.Element("SafePath").Value);
            var connstr = settings.Element("ConnectionString").Value;
            var JudgeUser = settings.Element("JudgeUser").Value;
            var JudgerPsw = settings.Element("JudgerPsw").Value;
            var judgercert = new KeyValuePair<string, string>(JudgeUser, JudgerPsw);
            //var safeCode = File.ReadAllText(settings.Element("SafeCode").Value);
            log("Heartbeat: {0} MS\r\nSafePath: {1}\r\nJudgeUser: {2}\r\nInitializing...\r\n", heartbeat, path, JudgeUser);
            var judger = new JudgeKernel.Judger(path, langs, judgercert);
            reader = new Reader(connstr, heartbeat);
            log("Ready.\r\n");

            while (true)
            {
                try
                {
                    var code = reader.WaitCode();
                    code.code = SafeProcess.Check(code.code);
                    logs("RID:{2}{0}, Username:[{1}], Status:[", code.rid, code.username, code.eid == null ? "" : "e(" + code.eid + ")");
                    var result = judger.Judge(code, statusFunc);
                    if (firstRun && result.status == 8 && string.IsNullOrEmpty(result.exeOutput))
                    {
                        log("{0},", status2String(-1));
                        firstRun = false;
                        result = judger.Judge(code, statusFunc);
                    }
                    log("{0}]", status2String(result.status));

                    reader.SaveResult(result);
                    if (result.status == 3) log(" Tme:{0}, Mem:{1}", result.exeTime, result.exeMem);
                    else if (result.status == 9) log(" {0} - {1}", result.reCode, reCode2String(result.reCode));
                    else if (result.status == 8) log(" outLen:{0}, rightLen:{1}", result.exeOutput.Length, code.outputaData.Length);
                    log("\r\n");
                } catch (Exception ex)
                {
                    logs(ex.Message + "\r\n");
                    log(ex.Source + "\r\n");
                    log(ex.StackTrace + "\r\n");
                    System.Threading.Thread.Sleep(5000);
                }
            }
        }

        static Reader reader;
        static void statusFunc(int i)
        {
            log(i + ",");
            reader.SetStatus(i);
        }

        static void logs(string str, params object[] args)
        {
            log(string.Format("{0}> {1}", DateTime.Now.ToShortTimeString(), str), args);
        }
        static string logpath;
        static void log(string str, params object[] args)
        {
            str = string.Format(str, args);
            Console.Write(str);
            var logfile = Path.Combine(logpath, DateTime.Now.ToString("yyyy_M_d") + ".log");
            File.AppendAllText(logfile, str);
        }

        static string status2String(int status)
        {
            switch (status)
            {
                case 3:
                    return "AC";
                case 4:
                    return "PE";
                case 5:
                    return "TLE";
                case 6:
                    return "MLE";
                case 7:
                    return "OLE";
                case 8:
                    return "WA";
                case 9:
                    return "RE";
                case 10:
                    return "CE";
                default:
                    return "SE";
            }
        }

        static string reCode2String(int? reCode)
        {
            if (!reCode.HasValue) return null;
            switch ((uint)reCode.Value)
            {
                case 0x80000001: return "GuardPageViolation";
                case 0x80000002: return "DataTypeMisalignmen";
                case 0x80000003: return "Breakpoint";
                case 0x80000004: return "SingleStep";
                case 0xC0000005: return "AccessViolation";
                case 0xC000001D: return "IllegalInstruction";
                case 0xC0000025: return "NonContinuableException";
                case 0xC0000026: return "InvaildDisposition";
                case 0xC000008C: return "ArrayBoundsExceeded";
                case 0xC000008D: return "FloatDenormalOperand";
                case 0xC000008E: return "FloatDivideByZero";
                case 0xC000008F: return "FloatInexactResult";
                case 0xC0000090: return "FloatInvalidOperation";
                case 0xC0000091: return "FloatOverflow";
                case 0xC0000092: return "FloatStackCheck";
                case 0xC0000093: return "FloatUnderflow";
                case 0xC0000094: return "IntegerDivideByZero";
                case 0xC0000095: return "IntegerOverflow";
                case 0xC0000096: return "PrivilegedInstruction";
                case 0xC00000FD: return "StackOverflow";
                case 0xC000013A: return "ControlCExit";
                case 0xC00002B4: return "FloatMultipleFaults";
                case 0xC00002B5: return "FloatMultipleTraps";
                case 0xC00002C9: return "RegNatConsumption";
                default: return "Unknow";
            }
        }
    }
}
